package com.leo;

import com.almasb.fxgl.app.CursorInfo;
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.app.scene.*;
import com.almasb.fxgl.core.math.FXGLMath;
import com.almasb.fxgl.core.util.LazyValue;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.level.Level;
import com.almasb.fxgl.entity.level.tiled.TMXLevelLoader;
import com.almasb.fxgl.time.TimerAction;
import com.leo.collisions.*;
import com.leo.common.Config;
import com.leo.common.GameType;
import com.leo.component.TankComponent;
import com.leo.entity.PlayerEntityFactory;
import com.leo.ui.*;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.util.Duration;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.net.MalformedURLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.almasb.fxgl.dsl.FXGL.*;
import static com.almasb.fxgl.dsl.FXGLForKtKt.getFileSystemService;

public class TankApp extends GameApplication {

    private TankComponent playerComponent;
    /**
     * 冻结时间
     */
    private TimerAction freezeEnemyTimerAction;
    /**
     * 加固时间
     */
    private TimerAction reinforceTimerAction;
    /**
     * 每隔3s生成一个坦克时间
     */
    private TimerAction spawnEnemyTimerAction;
    private Entity player;

    private final LazyValue<FailedScene> failedSceneLazyValue = new LazyValue<>(FailedScene::new);
    private final LazyValue<SuccessedScene> successedSceneLazyValue = new LazyValue<>(SuccessedScene::new);

    @Override
    protected void initSettings(GameSettings settings) {
        settings.setTitle("Tank");
        settings.setVersion("0.1");
        settings.setWidth(28 * Config.CELL_SIZE + 6 * Config.CELL_SIZE);
        settings.setHeight(28 * Config.CELL_SIZE);
        settings.setAppIcon("ui/icon.png");
        settings.getCSSList().add("tankApp.css");
        settings.setDefaultCursor(new CursorInfo("ui/cursor.png", 0, 0));
        settings.setGameMenuEnabled(true);
        settings.setMainMenuEnabled(true);

        // 场景重绘
        settings.setSceneFactory(new SceneFactory() {
            /**
             * 主菜单
             * @return
             */
            @NotNull
            @Override
            public FXGLMenu newMainMenu() {
                return new TankMainMenu();
            }

            /**
             * 加载场景
             * @return
             */
            @NotNull
            @Override
            public LoadingScene newLoadingScene() {
                return new TankLoadingScene();
            }

            /**
             * 启动场景
             * @param width
             * @param height
             * @return
             */
            @NotNull
            @Override
            public StartupScene newStartup(int width, int height) {
                return new TankStartupScene(width, height);
            }
        });
    }

    @Override
    protected void initInput() {
        FXGL.onKey(KeyCode.UP, () -> {
            if (playerIsActive()) {
                playerComponent.moveUp();
            }
        });
        FXGL.onKey(KeyCode.DOWN, () -> {
            if (playerIsActive()) {
                playerComponent.moveDown();
            }
        });
        FXGL.onKey(KeyCode.LEFT, () -> {
            if (playerIsActive()) {
                playerComponent.moveLeft();
            }
        });
        FXGL.onKey(KeyCode.RIGHT, () -> {
            if (playerIsActive()) {
                playerComponent.moveRight();
            }
        });
        FXGL.onKey(KeyCode.SPACE, () -> {
            if (playerIsActive()) {
                playerComponent.shoot();
            }
        });
    }

    @Override
    protected void initGame() {
        // 1.设置背景色为黑色
        FXGL.getGameScene().setBackgroundColor(Color.BLACK);

        // 2.创建游戏实体工厂类
        FXGL.getGameWorld().addEntityFactory(new PlayerEntityFactory());

        buildAndStartLevel();

        // 监听消灭敌人的数量
        FXGL.getip("destroyedEnemyAmount").addListener((ob, ov, nv) -> {
            if (nv.intValue() == Config.MAX_ENEMY_NUM) {
                play("Win.wav");
                FXGL.runOnce(() -> FXGL.getSceneService().pushSubScene(successedSceneLazyValue.get()), Duration.seconds(1));
            }
        });
    }

    public void buildAndStartLevel() {
        //1. 清理上一个关卡的残留(这里主要是清理声音残留)
        //清理关卡的残留(这里主要是清理声音残留)
        getGameWorld().getEntitiesByType(
                GameType.BULLET, GameType.ENEMY, GameType.PLAYER
        ).forEach(Entity::removeFromWorld);

        //2. 开场动画
        Rectangle rect1 = new Rectangle(getAppWidth(), getAppHeight() / 2.0, Color.web("#333333"));
        Rectangle rect2 = new Rectangle(getAppWidth(), getAppHeight() / 2.0, Color.web("#333333"));
        rect2.setLayoutY(getAppHeight() / 2.0);
        Text text = new Text("STAGE " + geti("level"));
        text.setFill(Color.WHITE);
        text.setFont(new Font(35));
        text.setLayoutX(getAppWidth() / 2.0 - 80);
        text.setLayoutY(getAppHeight() / 2.0 - 5);
        Pane p1 = new Pane(rect1, rect2, text);

        addUINode(p1);

        Timeline tl = new Timeline(
                new KeyFrame(Duration.seconds(1.2),
                        new KeyValue(rect1.translateYProperty(), -getAppHeight() / 2.0),
                        new KeyValue(rect2.translateYProperty(), getAppHeight() / 2.0)
                ));
        tl.setOnFinished(e -> removeUINode(p1));

        PauseTransition pt = new PauseTransition(Duration.seconds(1.5));
        pt.setOnFinished(e -> {
            text.setVisible(false);
            tl.play();
            //3. 开始新关卡
            startLevel();
        });
        pt.play();
    }

    @Override
    protected void initPhysics() {
        // 子弹和敌人的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new BulletEnemyHandler());
        // 子弹和玩家的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new BulletPlayerHandler());
        // 子弹和子弹的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new BulletBulletHandler());
        // 子弹和边界的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new BulletBorderHandler());

        // 子弹和土墙的碰撞效果
        BulletBrickHandler bulletBrickHandler = new BulletBrickHandler();
        FXGL.getPhysicsWorld().addCollisionHandler(bulletBrickHandler);
        // 子弹和石头的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(bulletBrickHandler.copyFor(GameType.BULLET, GameType.STONE));
        // 子弹和森林
        FXGL.getPhysicsWorld().addCollisionHandler(bulletBrickHandler.copyFor(GameType.BULLET, GameType.GREENS));

        // 玩家与道具的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new ItemPlayerHandler());
        // 敌人与道具的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new ItemEnemyHandler());

        // 子弹与旗帜的碰撞效果
        FXGL.getPhysicsWorld().addCollisionHandler(new BulletFlagHandler());
    }

    @Override
    protected void initGameVars(Map<String, Object> vars) {
        // 冻结标识
        vars.put("freezeEnemy", false);
        // 敌人数量
        vars.put("enemyNum", 3);
        // 消灭敌人的数量
        vars.put("destroyedEnemyAmount", 0);
        // 当前关卡
        if (getFileSystemService().exists(Config.CUSTOM_LEVEL_PATH)) {
            vars.put("level", 0);
        } else {
            vars.put("level", 1);
        }
    }

    /**
     * 冻结
     */
    public void freezeEnemy() {
        expireTimerAction(freezeEnemyTimerAction);
        FXGL.set("freezeEnemy", true);
        // 定时器
        freezeEnemyTimerAction = FXGL.runOnce(() -> FXGL.set("freezeEnemy", false), Duration.seconds(10));
    }

    /**
     * 加固基地
     */
    public void reinforce() {
        expireTimerAction(reinforceTimerAction);
        reinforceTimerAction = FXGL.runOnce(() -> Config.POINT_2_D_LIST.forEach(point2D -> {
            List<Entity> entitiesAt = FXGL.getGameWorld().getEntitiesAt(point2D);
            entitiesAt.forEach(Entity::removeFromWorld);
            Entity stone = FXGL.spawn("brick", point2D);
            stone.getViewComponent().addChild(FXGL.texture("map/brick.png"));
        }), Duration.seconds(15));
        Config.POINT_2_D_LIST.forEach(point2D -> {
            List<Entity> entitiesAt = FXGL.getGameWorld().getEntitiesAt(point2D);
            entitiesAt.forEach(Entity::removeFromWorld);
            Entity stone = FXGL.spawn("stone", point2D);
            stone.getViewComponent().addChild(FXGL.texture("map/stone.png"));
        });
    }

    /**
     * 不加固
     */
    public void unReinforce() {
        Config.POINT_2_D_LIST.forEach(point2D -> {
            List<Entity> entitiesAt = FXGL.getGameWorld().getEntitiesAt(point2D);
            entitiesAt.forEach(Entity::removeFromWorld);
        });
    }

    /**
     * 失效
     *
     * @param timerAction
     */
    private void expireTimerAction(TimerAction timerAction) {
        if (Objects.nonNull(timerAction) && !timerAction.isExpired()) {
            timerAction.expire();
        }
    }

    /**
     * 对象是否存在且活跃
     *
     * @return
     */
    public boolean playerIsActive() {
        return Objects.nonNull(player) && player.isActive();
    }

    public void gameOver() {
        FXGL.getSceneService().pushSubScene(failedSceneLazyValue.get());
    }

    /**
     * 下一关
     */
    public void startLevel() {
        // 重置
        expireTimerAction(freezeEnemyTimerAction);
        expireTimerAction(reinforceTimerAction);
        expireTimerAction(spawnEnemyTimerAction);
        // 冻结标识
        FXGL.set("freezeEnemy", false);
        // 敌人数量
        FXGL.set("enemyNum", 3);
        // 消灭敌人的数量
        FXGL.set("destroyedEnemyAmount", 0);

        // 3.加载地图，地图中不设置对象其实就是一张图片
        //如果关卡是 0 ,那么从用户自定义地图开始游戏
        if (FXGL.geti("level") == 0) {
            Level level;
            try {
                level = new TMXLevelLoader()
                        .load(new File(Config.CUSTOM_LEVEL_PATH).toURI().toURL(), FXGL.getGameWorld());
                FXGL.getGameWorld().setLevel(level);
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        } else {
            FXGL.setLevelFromMap("level" + FXGL.geti("level") + ".tmx");
        }

        play("start.wav");

        // 4.创建游戏实体并添加到游戏世界中
        player = FXGL.spawn("player", 100, 600);
        playerComponent = player.getComponent(TankComponent.class);

        // 创建右侧信息面板
        GameView gameView = new GameView(new InfoPane(), Integer.MAX_VALUE);
        FXGL.getGameScene().addGameView(gameView);

        // 创建敌人
        FXGL.spawn("enemy", 30, 30);
        FXGL.spawn("enemy", 330, 30);
        FXGL.spawn("enemy", 580, 30);

        // 每隔3s生成一个坦克
        spawnEnemyTimerAction = FXGL.run(() -> {
            if (FXGL.geti("enemyNum") == Config.MAX_ENEMY_NUM) {
                expireTimerAction(spawnEnemyTimerAction);
                return;
            }

            // 查找范围内是否存在坦克、玩家、土墙、水、石头，不存在才可以随机生成
            Point2D point2D = FXGLMath.random(Config.SPAWN_ENEMY_POSITION).get();
            List<Entity> entitiesInRange = FXGL.getGameWorld().getEntitiesInRange(new Rectangle2D(point2D.getX(), point2D.getY(), 39, 39));
            List<Entity> entities = entitiesInRange.stream().filter(entity -> entity.isType(GameType.PLAYER) || entity.isType(GameType.ENEMY) || entity.isType(GameType.STONE) || entity.isType(GameType.BRICK) || entity.isType(GameType.SEA)).toList();
            if (entities.isEmpty()) {
                FXGL.spawn("enemy", point2D);
                // 坦克数量加1
                FXGL.inc("enemyNum", 1);
            }
        }, Duration.seconds(3));
    }

    public static void main(String[] args) {
        launch(args);
    }
}
